#include "herm/base/utils/misc.h"
#include "address.h"
#include "datagram.h"

namespace herm{
Datagram::Datagram()
{
	Open();
}

Datagram::Datagram(const Address& addr)
{
	Open(addr);
}

Datagram::~Datagram(void)
{
}

int Datagram::Receive(char* buffer, int length)
{	
	HERM_ASSERT (IsBlock());

	int ret = recvfrom(GetHandle(), buffer, length, 0, nullptr, nullptr);
	if (ret == Private::FAILED)
		throw HERM_SOCKET_EXCEPTION(ST_RECV_FAILED);

	return ret;
}
	
int Datagram::Receive(char* buffer, int length, Address& addr)
{	
	HERM_ASSERT (IsBlock());

	SockLen len = sizeof(sockaddr);
	int ret = recvfrom(GetHandle(), buffer, length, 0, (sockaddr*)&addr.SockAddr(), &len);
	if (ret == Private::FAILED)
		throw HERM_SOCKET_EXCEPTION(ST_RECV_FAILED);

	return ret;
}

void Datagram::Send(const char* buffer, int length, const Address& addr)	
{
	HERM_ASSERT (IsBlock());

	int ret = sendto(GetHandle(), buffer, length, 0, (const sockaddr*)&addr.SockAddr(),
		sizeof(sockaddr));

	if (ret == Private::FAILED)	
		throw HERM_SOCKET_EXCEPTION(ST_SEND_FAILED);
}

int Datagram::ReceiveNB(char* buffer, int length)
{	
	HERM_ASSERT (!IsBlock());

	int ret = recvfrom(GetHandle(), buffer, length, 0, nullptr, nullptr);
	if (ret == Private::FAILED && Private::GetLastError() != Private::HEWOULDBLOCK)
		throw HERM_SOCKET_EXCEPTION(ST_RECV_FAILED);

	return ret;
}
	
int Datagram::ReceiveNB(char* buffer, int length, Address& addr)
{	
	HERM_ASSERT (!IsBlock());

	SockLen len = sizeof(sockaddr);
	int ret = recvfrom(GetHandle(), buffer, length, 0, (sockaddr*)&addr.SockAddr(), &len);
	if (ret == Private::FAILED && Private::GetLastError() != Private::HEWOULDBLOCK 
		&& Private::GetLastError() != Private::HECONNRESET) // udp of Windows ECONNRESET is "ICMP Port Unreachable"
		throw HERM_SOCKET_EXCEPTION(ST_RECV_FAILED);

	return ret;
}

int Datagram::SendNB(const char* buffer, int length, const Address& addr)	
{
	HERM_ASSERT (!IsBlock());

	int ret = sendto(GetHandle(), buffer, length, 0, (const sockaddr*)&addr.SockAddr(),
		sizeof(sockaddr));

	if (ret == Private::FAILED && Private::GetLastError() != Private::HEWOULDBLOCK)
		throw HERM_SOCKET_EXCEPTION(ST_SEND_FAILED);

	return ret;
}
}

